<template>
  <xhyk-scroll v-bind="$attrs">
  <div class="treeX" ref="treeXDom"
       :style="{'--displayX': props.display}" :class="{highlight: props.highlight}">
      <el-tree ref="treeObj" :style="props.style" :data="props.data" :props="propD"
               :empty-text="props.emptyText" :default-expand-all="showAll"
               :load="loadFn" :lazy="openLazy" :node-key="keyId" :filter-node-method="searchNode"
               :default-checked-keys="defaultExpanded" :expand-on-click-node="props.clickToOpen"
               @node-expand="nodeExpand" @node-click="nodeClick" :show-checkbox="props.showCheck"
               @check-change="checkChangeFn" @check="checkFn">
        <template #default="{node,data}">
          <slot name="default" :node="node" :data="data" :style="props.childStyle">
            <span style="padding: 0px 5px;">{{ data[props.props.label] }}</span>
          </slot>
        </template>
      </el-tree>
  </div>
  </xhyk-scroll>
</template>

<script setup>
import {computed, onMounted, ref, watch,nextTick,onUnmounted} from 'vue'
import { ElTree } from 'element-plus'
import XhykScroll from "./xhykScroll.vue";

/* 树形结构
* level：代表第几层
* isLeaf: 代表是否还有下一级，显示下拉箭头
* loaded: 赋值为false时，则在这层还没执行懒加载load函数
* expand(): 执行一次懒加载函数，替换旧子节点数据。如果当前父节点是显示状态，会自动显示子节点
* isCurrent: 当前节点选中状态。
* expanded: true：当前节点展开。
* visible: true时显示该节点
*  */

const emit = defineEmits(['click', 'check', 'checkChange'])

const props = defineProps({

  // 第一层数据{}结构。传入data时则不启用懒加载
  data: {
    type: Object,
    default: undefined
  },

  // 将以后的数组数据换成懒加载的数据(暂不起作用)
  dataTree: {
    type: [Object, undefined],
    default: undefined
  },

  // 父级样式
  style: {
    type: [Object, String],
    default: {}
  },

  // 子级样式
  childStyle: {
    type: [Object, String],
    default: {}
  },

  // 设置字段对应：结构树的字段 -》 数据字段
  props: {
    type: Object,
    default: {
      value: 'id',
      label: 'label',
      children: 'children',
      isLeaf: 'leafe'  // 是否有下一层,true为没有,不再显示下标符合
    }
  },

  // 结构树的唯一标识，对应数据唯一字段。通常是对应props数据字段的value值
  keyId: {
    type: String,
    default: ''
  },

  // 空数据时的提示文本
  emptyText: {
    type: String,
    default: '没有更多数据'
  },

  // 懒加载数据处理函数，需要返回一个数组。node.level 判断当前是在第几层
  load: {
    type: [Function, undefined],
    default: undefined
    // default: function (node) {
    //   // console.log('当前节点',node)
    //   return []
    // }
  },

  // 开启懒加载。通常load传函数则开启懒加载，但如果是第一层不需要懒加载则配合lazy，在第一层data数据后再改变lazy的值
  lazy: {
    type: [Boolean, undefined],
    default: undefined
  },

  // 设置当前选中的节点样式
  highlight: {
    type: Boolean,
    default: true
  },

  // 根据这个文本内容，去过滤节点
  search: {
    type: String,
    default: ''
  },

  // 过滤文本是否不区分大小写
  like: {
    type: Boolean,
    default: false
  },

  // 根据指定值设置显示的结构层
  floor: {
    type: Object,
    default: {
      show: false, // 如果show为true则开启。但节点对应的field字段等于这个value时则显示
      field: '', // 如果field不为''则开启。但节点对应的field字段等于这个value时则显示
      value: '',
      order: true // true时从小到大排序
    }
  },

  // 点击节点是否展开
  clickToOpen: {
    type: Boolean,
    default: false
  },

  // 默认全部展开
  showAll: {
    type: Boolean,
    default: false
  },

  // 是否显示选择框
  showCheck: {
    type: Boolean,
    default: false
  },

  //节点的display样式
  display: {
    type: String,
    default: 'table-header-group'
  }
})

// 处理prop懒加载字段
const propD = computed(() => {
  const result = {
    value: props.props.value || 'id',
    label: props.props.label || 'label',
    children: props.props.children || 'children',
    isLeaf: props.props.isLeaf || 'leafe'
  }
  return result
})

// 判断是否开启懒加载
const openLazy = computed(() => {
  if (props.lazy == undefined) {
    // 判断懒加载函数
    return props.load != undefined
  } else {
    return props.lazy
  }
})

// 第一个结构树对象
const treeObj = ref({})

// 结构树被展开了
const nodeExpand = () => {
  // console.log('结构树被展开了')
}

// 结构树被点击了。好像触发了两次.300毫秒内只能触发一次
const clickId = ref(-1)
const nodeClick = (data, node, event) => {
  if (clickId.value) {
    clearTimeout(clickId.value)
    clickId.value = -1
  }
  clickId.value = setTimeout(() => {
    emit('click', {
      data,
      node,
      event
    })
  }, 300)
}


// 机构树复选框被勾选
const checkFn = (data, checkNodes) => {
  // console.log(data,checkNodes)
  const checkList = checkNodes.checkedNodes
  const haveThis = checkList.find(item => item.$treeNodeId == data.$treeNodeId)
  let toCheck = Boolean(haveThis)
  emit('check', {
    data,
    toCheck,
    checkList
  })
}
// 机构树复选框被修改
const checkChangeFn = (data, toCheck, haveChild) => {
  // console.log(data,toCheck,haveChild)

}
// 设置勾选
const setCheck = nodesList => {
  treeObj.value.setCheckedNodes(nodesList)
}

// 保存根节点
let firstNode = {}

// 删除节点(参数对应原始props数据字段的value值)。暴露到父组件
const delNode = nodeValue => {
  // console.log('删除节点', nodeValue)
  treeObj.value.remove(nodeValue)
}

// 获取某个节点的数据，onlyData：只返回data数据，不包含节点属性。暴露给父组件
const getNode = (nodeValue, onlyData = false) => {
  const nodeObj = treeObj.value.getNode(nodeValue)
  return !onlyData ? nodeObj : nodeObj.data
}

// 给某个节点添加子节点data数据（注意如果该节点没有懒加载过，会出现数据更新不及时情况）。暴露给父组件
const addNode = (nodeValue, data) => {
  treeObj.value.append(data, nodeValue)
}

// 设置当前选中的节点
const chooseNode = (nodeValue, pass = false) => {
  // 获取对应节点
  let nodeObj = treeObj.value.getNode(nodeValue)
  if (nodeObj) {
    treeObj.value.setCurrentNode(nodeObj.data, true)
  } else if (pass) {
    treeObj.value.setCurrentNode(nodeValue, true)
  }
}

// 更新某个节点的子节点数据。toChoose：是否选中当前父节点
const updata = (fatherValue, toChoose = true) => {
  const father = treeObj.value.getNode(fatherValue)
  if (father) {
    father.loaded = false // 设置这个父节点为没执行过懒加载
    father.expand() // 执行懒加载，自动会展开新的子节点数据
    if (toChoose) {
      // 自动选中该父节点
      chooseNode(father.data)
    }
  }
}

// 取当前选中的节点
const getChoose = () => {
  return treeObj.value.getCurrentNode()
}

// 更新整个树形结构
const reLoad = () => {
  firstNode.loaded = false // 修改懒加载记录
  firstNode.expand() // 重新懒加载
}

// 移动节点
const changeNode = (nodeValue, data) => {

}


// 默认展开的节点
const defaultExpanded = ref([])
// 设置节点的key属性，对应数据唯一字段。默认为props字段的value值
const keyId = ref(Boolean(props.keyId) ? props.keyId : props.props.value)

/* 根据文本，过滤节点 */
/* 1、过滤显示层。 */
// 根据指定值。通过过滤显示指定结构层
const filterData = computed(() => {
  // 如果show为true则开启。但节点对应的field字段等于这个value时则显示
  let result = {show: false, field: '', value: '', order: true}
  result.show = Boolean(props.floor.field) ? true : false
  result.field = props.floor.field || ''
  result.value = props.floor.value || ''
  result.order = (props.floor.order === undefined || Boolean(props.floor.order) == true) ? true : false // 不填或order为true时，为true
  return result
})
watch(filterData, (newValue) => {
  defaultExpanded.value = [] // 取消当前默认展开
  treeObj.value.filter(props.search)
}, {
  deep: true
})
const filterFn = (data) => {
  if (filterData.value.order) {
    // 这个节点的对应字段的值小于或等于当前要显示的层才显示
    return data[filterData.value.field] <= filterData.value.value
  } else {
    // 这个节点的对应字段的值大于或等于当前要显示的层才显示
    return data[filterData.value.field] >= filterData.value.value
  }
}

/* 2、过滤节点文本 */
// 监听父组件文本变化，调用tree组件的过滤方法
watch(() => props.search, newValue => {
  defaultExpanded.value = [] // 取消当前默认展开
  treeObj.value.filter(newValue)
})

// 过滤处理。返回ture则显示
const searchNode = (text, data, node) => {
  let filterResult = true
  // 开启了过滤层
  if (filterData.value.show) {
    filterResult = filterFn(data)
  }
  // 空文本时
  if (text == '') {
    // 折叠节点
    // node.expanded = false
    return filterResult
  }
  console.log(234,filterResult)

  // 非空文本时
  let tempResult = false
  if (props.like){
    // 不区分大小写
    tempResult = data[props.props.label].toLowerCase().indexOf(text.toLowerCase()) != -1
  }else {
    tempResult = data[props.props.label].indexOf(text) != -1
  }
  if (tempResult) {
    // return true渲染节点后，再展开
    if (filterResult) {
      setTimeout(() => {
        // 通过选中张开节点
        treeObj.value.setCurrentNode(data)
        // 取消选中状态
        try {
          treeObj.value.setCurrentNode({})
        } catch (err) {
        }
      }, 500)
    }
    return filterResult
  } else {
    return false
  }
}

// 懒加载函数
const loadFn = async (node, resolve) => {
  // 保存根节点
  if (node.level == 0) {
    firstNode = node
  }
  const result = await props.load(node, node.data, resolve)
  if (result instanceof Array && result.length > 0) {
    // 如果是开启过滤层，则延时后过滤
    if (filterData.value.show) {
      setTimeout(() => {
        node.childNodes.forEach(item => {
          item.visible = filterFn(item.data)
        })
      }, 100)
    }
    resolve(result)
  } else {
    resolve([])
  }
}

defineExpose({
  delNode,
  getNode,
  addNode,
  updata,
  chooseNode,
  getChoose,
  reLoad,
  firstNode,
  setCheck
})


// 获取元素信息
const getStyle = domObj => {
  if (domObj) {
    if (domObj.currentStyle) {
      return domObj.currentStyle
    } else {
      return window.getComputedStyle(domObj)
    }
  }
}

// region 修改组件宽度百分百撑满
// 修改组件宽度百分百撑满
const minWidth = ref('')

const treeXDom = ref({})

const getWH = () => {
  nextTick(() => {
    const temp = window.getComputedStyle(treeXDom.value)
    if (temp.width){
      minWidth.value = `${parseFloat(temp.width)}px`
    }
  })
}

// endregion

onMounted(() => {
  getWH()
  window.addEventListener('resize', getWH)
})

onUnmounted(() => {
  window.removeEventListener('resize', getWH)
})
</script>

<style scoped lang='scss'>
// 树形结构选中项
.highlight {
  // 是否高亮显示当前选中
  :deep(.is-current) > .el-tree-node__content {
    color: var(--menuSysColor);
    background-color: var(--menuSysColor2);
  }
}

.treeX {
  width: 100%;
  margin-bottom: 10px;
  // 溢出部分也显示
  .el-tree {
    width: 100% !important;
  }

  :deep(.el-tree-node) {
    width: fit-content;
    min-width: v-bind(minWidth) !important;
  }
}
</style>